<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
		"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Destroyable unit test</title>

<script src="boilerplate.js"></script>

<script type="text/javascript">
require([
	"doh/runner",
	"dojo/aspect", "dojo/_base/declare", "dojo/Deferred", "dojo/dom", "dojo/dom-construct",
	"dojo/_base/lang", "dojo/on", "dojo/Stateful",
	"dijit/Destroyable", "dojo/domReady!"
], function(doh, aspect, declare, Deferred, dom, domConstruct, lang, on, Stateful, Destroyable){
	doh.register("Destroyable", [
		function general(){
			var SupportingWidget = declare([], {
				destroyCalls: 0,
				constructor: function(name){
					this.name = name;
				},
				destroy: function(){
					this.destroyCalls++;
				}
			});
			var watchMe = new Stateful({
				name: "watchMe",
				x: 0
			});
			var DestroyableSubClass = declare(Destroyable, {
				// number of times my button was clicked
				clicks: 0,

				// number of times watchMe changed value of x
				watches: 0,

				constructor: function(){
					var self = this;
					this.domNode = domConstruct.create("button");
					this.own(
						// setup an event handler (to be destroyed when I'm destroyed)
						on(this.domNode, "click", function(){ self.clicks++; }),

						// watch external watchMe class (to be unwatch()'d when I'm destroyed)
						watchMe.watch("x", function(name, oVal, nVal){ self.watches++; })
					);

					// Setup two supporting widgets, to be destroyed when I'm destroyed
					this.own(this.sw1 = new SupportingWidget("sw1"));
					this.own(this.sw2 = new SupportingWidget("sw2"));
				}
			});

			var destroyable1 = new DestroyableSubClass();
			dom.byId("container").appendChild(destroyable1.domNode);

			// make sure event handler was setup
			destroyable1.domNode.click();
			doh.is(1, destroyable1.clicks, "one click");

			// make sure watch handler was setup
			watchMe.set("x", 1);
			doh.is(1, destroyable1.watches, "one watch notification");

			// manually destroy one of the supporting widgets
			destroyable1.sw1.destroy();
			doh.is(1, destroyable1.sw1.destroyCalls);
			
			// Destroy the Destroyable instance itself.   destroyable1 should:
			// 		- destroy the sw2 supporting widget, but not try to re-destroy sw1
			//		- disconnect the watch() listener on watchMe
			//		- disconnect the click event handler on destroyable1.domNode
			destroyable1.destroy();
			doh.is(1, destroyable1.sw1.destroyCalls, "sw1 wasn't redestroyed");
			doh.is(1, destroyable1.sw2.destroyCalls, "sw2 was destroyed");
			
			destroyable1.domNode.click();
			doh.is(1, destroyable1.clicks, "no new click notification");
			watchMe.set("x", 2);
			doh.is(1, destroyable1.watches, "no new watch notification");
		},

		function multipleDestroyFunctions(){
			var destroyRecursiveCount = 0;
			var destroyCount = 0;
			var W1 = declare([Destroyable], {
				destroyRecursive: function() {
					destroyRecursiveCount++;
					this.destroy();
					this.inherited(arguments);
				},
				destroy: function() {
					destroyCount++;
					this.inherited(arguments);
				}
			});
			var W2 = declare([Destroyable], {
				test: function() {
					var w1 = new W1();
					this.own(w1);
					w1.destroy();
				},
				destroy: function() {
					this.inherited(arguments);
				}
			});
			var W3 = declare([Destroyable], {
				test: function() {
					var w1 = new W1();
					this.own(w1);
					w1.destroyRecursive();
				},
				destroy: function() {
					this.inherited(arguments);
				}
			});
			var w2 = new W2();
			w2.test();
			w2.destroy();
			doh.is(0, destroyRecursiveCount, "destroyRecursive was not called as expected");
			doh.is(1, destroyCount, "destroy was not called as expected");
			destroyRecursiveCount = 0;
			destroyCount = 0;
			var w3 = new W3();
			w3.test();
			w3.destroy();
			doh.is(1, destroyRecursiveCount, "destroyRecursive was not called as expected");
			doh.is(1, destroyCount, "destroy was not called as expected");
		},

		function owningPromises(){
			var cancels = 0;
			var W1 = declare(Destroyable, {
				constructor: function(){
					this.p1 = new Deferred(function(){ console.log("cancel p1"); cancels++; });
					this.p2 = new Deferred(function(){ console.log("cancel p2"); cancels++; });
					this.p3 = new Deferred(function(){ console.log("cancel p3"); cancels++; });
					this.p4 = new Deferred(function(){ console.log("cancel p4"); cancels++; });
					this.own(this.p1, this.p2, this.p3, this.p4);
				}
			});

			var w1 = new W1();

			w1.p1.resolve(true);
			aspect.after(w1.p1, "cancel", function(){
				throw new Error("p1 shouldn't have been canceled");
			}, true);

			w1.p2.reject(new Error("I was rejected"));
			aspect.after(w1.p2, "cancel", function(){
				throw new Error("p2 shouldn't have been canceled");
			}, true);

			w1.p3.cancel();
			doh.is(1, cancels, "one promise canceled manually before destroy");

			// Destroying the widget should only cancel p4; it's the only Promise that hasn't been dealt with already.
			// OTOH if Destroyable is broken, one of the asserts above may go off during the destroy() call.
			w1.destroy();

			doh.is(2, cancels, "only p4 canceled on widget destroy");
		}

	]);	// doh.register()

	doh.run();
});	// require()

</script>
</head>
<body>
<h1>dijit/Destroyable Unit Test</h1>
<div id="container"></div>
</body>
</html>
